/*! \file ShaderBuilder.cpp
 *	\brief 
 *
 *	created:	2010/04/25
 *	created:	25:4:2010   15:43
 *	filename: 	c:\Users\venn\Documents\tactics-engine\libs\gd_dx11\ShaderBuilder.cpp
 *	file path:	c:\Users\venn\Documents\tactics-engine\libs\gd_dx11
 *	file base:	ShaderBuilder
 *	file ext:	cpp
 *	author:		Vitalii Trubchaninov
 *				venngraydog@gmail.com
 */

#include "gd_dx11/stdafx.h"
#include "gd_dx11/ShaderBuilder.h"

#include <D3Dcompiler.h>

#include <gd/InputLayoutDesc.h>
#include <gd/PixelShader.h>
#include <gd/VertexShader.h>

#include <resource_manager/ResourceManager.h>

#include <utils/MemoryBuffer.h>

#include "gd_dx11/Device.h"
#include "gd_dx11/PixelShaderImpl.h"
#include "gd_dx11/InputLayout.h"
#include "gd_dx11/VertexShaderImpl.h"

#pragma comment(lib, "d3dcompiler")

namespace tt{
namespace gd_dx11{

//////////////////////////////////////////////////////////////////////////

const Char* const FX_FILENAME_TAG = "FXFile";
const Char* const FX_ENTRY_POINT_TAG = "EntryPoint";
const Char* const FX_VERSION_TAG = "Version";

const char* const FX_VS_INPUT_LAYOUT_TAG = "InputLayout";

//////////////////////////////////////////////////////////////////////////
ShaderBuilder::ShaderBuilder( Device* device )
: m_device( device)
{

}

ShaderBuilder::~ShaderBuilder()
{

}

SharedPtr<gd::PixelShader> ShaderBuilder::onCreatePixelShader( const utils::PTree& desc )
{
	const String pixelShaderPath = desc.get<String>(FX_FILENAME_TAG);
	const String pixelShaderEP = desc.get<String>(FX_ENTRY_POINT_TAG);
	const String pixelShaderVersion = desc.get<String>(FX_VERSION_TAG);

	SharedPtr<io::BaseIStream> is = ttResourceManager().GetStream(pixelShaderPath);
	if(!is)
		return SharedPtr<gd::PixelShader>();

	utils::COMPointer<ID3D10Blob> blobPS;
	if (!CompileShaderFromFile(*is, pixelShaderEP.c_str(), pixelShaderVersion.c_str(), blobPS))
		return SharedPtr<gd::PixelShader>();

	utils::COMPointer<ID3D11PixelShader> d3dPixelShader;
	HRESULT hr = m_device->d3dDevice()->CreatePixelShader( blobPS->GetBufferPointer(), blobPS->GetBufferSize(), NULL, d3dPixelShader.getRef() );
	if( FAILED( hr ) )
		return SharedPtr<gd::PixelShader>();

	SharedPtr<PixelShaderImpl> pixelShaderImpl( new PixelShaderImpl(d3dPixelShader) );
	return SharedPtr<gd::PixelShader>( new gd::PixelShader(pixelShaderImpl) );
}

SharedPtr<gd::VertexShader> ShaderBuilder::onCreateVertexShader( const utils::PTree& desc )
{
	SharedPtr<gd::VertexShader> result;

	const String vertexShaderPath = desc.get<String>(FX_FILENAME_TAG);
	const String vertexShaderEP = desc.get<String>(FX_ENTRY_POINT_TAG);
	const String vertexShaderVersion = desc.get<String>(FX_VERSION_TAG);

	SharedPtr<io::BaseIStream> is = ttResourceManager().GetStream(vertexShaderPath);
	if(!is)
		return result;

	utils::COMPointer<ID3D10Blob> blobVS;
	if (!CompileShaderFromFile(*is, vertexShaderEP.c_str(), vertexShaderVersion.c_str(), blobVS))
		return result;

	utils::COMPointer<ID3D11VertexShader> d3dVertexShader;
	HRESULT hr = m_device->d3dDevice()->CreateVertexShader( blobVS->GetBufferPointer(), blobVS->GetBufferSize(), NULL, d3dVertexShader.getRef() );
	if( FAILED( hr ) )
		return result;

	gd::InputLayoutDesc inputLayoutDesc;
	if(!inputLayoutDesc.Create( desc.get_child(FX_VS_INPUT_LAYOUT_TAG) ) )
		return result;

	gd_dx11::InputLayout inputLayout;
	if(!inputLayout.Create(inputLayoutDesc))
		return result;

	utils::COMPointer<ID3D11InputLayout> d3dInputLayout;
	m_device->d3dDevice()->CreateInputLayout(
		inputLayout.data(), inputLayout.size(),
		blobVS->GetBufferPointer(), blobVS->GetBufferSize(),
		d3dInputLayout.getRef() );

	SharedPtr<VertexShaderImpl> vertexShaderImpl( new VertexShaderImpl(d3dVertexShader, d3dInputLayout) );
	result.reset(new gd::VertexShader(vertexShaderImpl) );

	return result;
}

//////////////////////////////////////////////////////////////////////////
Bool ShaderBuilder::CompileShaderFromFile( io::BaseIStream& is, String entryPoint, String shaderModel, utils::COMPointer<ID3DBlob>& blobOut )
{
	utils::MemoryBuffer mb;
	mb.create(is);

	// Compile and create the vertex shader
	DWORD dwShaderFlags = D3D10_SHADER_ENABLE_STRICTNESS;
#if defined(TT_DEBUG)
	dwShaderFlags |= D3D10_SHADER_DEBUG;
#endif

	utils::COMPointer<ID3D10Blob> pBlobError;
	HRESULT hr = D3DCompile( 
		mb.data(), mb.size(), 
		"none", NULL, NULL, 
		entryPoint.c_str(), shaderModel.c_str(), 
		dwShaderFlags, 0, 
		blobOut.getRef(), pBlobError.getRef() );

	if( SUCCEEDED( hr ) )
		return true;

	if( pBlobError != NULL )
	{
		TT_OUTPUT( (const Char*)pBlobError->GetBufferPointer() );
	}
	return false;
}

} //namespace gd_dx11
} //namespace tt

